import SwiftUI
import WidgetKit
import CalendarShared
import WrappingHStack

public struct DayView: View {
    public enum Style {
        case date(TimelineEntry & CalendarEntryable)
        case taboo(LargeEntry)
    }

    public var style: Style
    @Environment(\.widgetFamily) var family
    @Environment(\.widgetRenderingMode) var renderingMode
    @Environment(\.colorScheme) var colorScheme

    public init(style: Style) {
        self.style = style
    }

    public var body: some View {
        switch style {
            case .date(let entry): dateView(entry)
            case .taboo(let entry): tabooView(entry)
        }
    }
}

private extension DayView {
    func dateView(_ entry: TimelineEntry & CalendarEntryable) -> some View {
        var dayModel: DayModel
        if entry is SmallEntry || family == .systemMedium {
            dayModel = entry.dayModel
        } else {
            dayModel = CalendarShared.shared.getDay(entry.date)
        }
        return VStack(spacing: 0) {
            dateHeader(entry, dayModel: dayModel)
            Text(String(dayModel.date.day))
                .font(.system(size: 60)
                .bold())
                .foreground(color: normalColor(entry))
                .widgetAccentable(renderingMode == .accented)
            if !dayModel.lunar.isEmpty, entry.showLunar {
                HStack {
                    if dayModel.isFestival {
                        entry.festival.color(colorScheme).frame(width: 2, height: 14).cornerRadius(1)
                        Text(dayModel.lunar).font(.system(size: 14))
                            .foregroundColor(entry.festival.color(colorScheme))
                            .widgetAccentable(renderingMode == .accented)
                    } else {
                        let lunar = dayModel.date.lunarMonthString + dayModel.date.lunarDayString(false)
                        entry.normal.color(colorScheme).frame(width: 2, height: 14).cornerRadius(1)
                        Text(lunar).font(.system(size: 14))
                            .foregroundColor(entry.normal.color(colorScheme))
                            .widgetAccentable(renderingMode == .accented)
                    }
                    Spacer()
                }
            }
        }
        .padding(.all, 8)
    }

    func dateHeader(_ entry: TimelineEntry & CalendarEntryable, dayModel: DayModel) -> some View {
        let month = NSLocalizedString("BearCalendar.calendar.month", bundle: moduleBundle, comment: "")
        let format = entry.language.isEnglish ? "MMM yyyy" : "M\(month) yyyy"
        var title = dayModel.date.formated(.other(format), language: entry.language)
        var week = Week.allCases[dayModel.date.weekday].title(entry.language)
        if !entry.language.isEnglish {
            week = "星期" + week
        }
        return HStack {
            VStack(alignment: .leading) {
                Text(title).font(.system(size: 14))
                    .foreground(color: normalColor(entry))
                    .widgetAccentable(renderingMode == .accented)
                Text(week).font(.system(size: 14))
                    .foreground(color: weekColor(entry, dayModel: dayModel))
                    .widgetAccentable(renderingMode == .accented)
            }
            Spacer()
            if entry.showFestival, dayModel.tiaoxiu != .none {
                Image(dayModel.tiaoxiu == .rest ? "rest" : "work")
                    .resizable()
                    .frame(width: 20, height: 22)
                    .foreground(color: dayModel.tiaoxiu == .rest ? entry.rest.color(colorScheme) : entry.work.color(colorScheme))
            }
        }
    }
}

private extension DayView {
    @ViewBuilder
    func tabooView(_ entry: LargeEntry) -> some View {
        var suitable = entry.date.suitable.prefix(count: 3, maxLength: 2)
        var taboo = entry.date.taboo.prefix(count: 3, maxLength: 2)
        if family == .systemMedium {
            let suitable = entry.dayModel.date.suitable.prefix(count: 3, maxLength: 2)
            let taboo = entry.dayModel.date.taboo.prefix(count: 3, maxLength: 2)
        }
        VStack(alignment: .leading, spacing: 4) {
            tabooHeader(entry)
            sectionHeader(entry, title: "宜", color: entry.taboo.color(colorScheme))
            tagView(suitable, color: entry.taboo.color(colorScheme))
            sectionHeader(entry, title: "忌", color: entry.avoid.color(colorScheme))
            tagView(taboo, color: entry.avoid.color(colorScheme))
        }
    }

    func tabooHeader(_ entry: LargeEntry) -> some View {
        let month = NSLocalizedString("BearCalendar.calendar.month", bundle: moduleBundle, comment: "")
        var format = entry.language.isEnglish ? "MMM yyyy" : "M\(month) yyyy"
        var title = entry.date.formated(.other(format), language: entry.language)
        var week = Week.allCases[entry.date.weekday].fullName(entry.language)
        if entry.language.isEnglish {
            week = Week.allCases[entry.date.weekday].title(entry.language)
        }
        if family == .systemSmall {
            title = entry.dayModel.date.formated(.other(format), language: entry.language)
            week = Week.allCases[entry.dayModel.date.weekday].fullName(entry.language)
            if entry.language.isEnglish {
                week = Week.allCases[entry.dayModel.date.weekday].title(entry.language)
            }
            if entry.dayModel.tiaoxiu != .none {
                format = entry.language.isEnglish ? "MMM" : "M\(month)"
            }
        }
        let smallEntry = SmallEntry(date: entry.date, configuration: entry.configuration)

        return HStack(spacing: 0) {
            Text(String(entry.dayModel.date.day))
                .font(.system(size: 30).bold())
                .foreground(color: normalColor(smallEntry))
                .widgetAccentable(renderingMode == .accented)
            VStack(spacing: entry.language.isEnglish ? 0 : 2) {
                ForEach(Array(week), id: \.self) { char in
                    Text(String(char))
                    .font(.system(size: 12, weight: .medium))
                    .foreground(color: weekColor(entry, dayModel: entry.dayModel))
                    .widgetAccentable(renderingMode == .accented)
                }
            }
            .padding(.leading, 3)
            VStack(alignment: .leading) {
                Text(title).font(.system(size: 14))
                    .foreground(color: normalColor(smallEntry))
                    .widgetAccentable(renderingMode == .accented)
                if entry.dayModel.isFestival {
                    Text(entry.dayModel.lunar)
                    .font(.system(size: 12, weight: .medium))
                    .foregroundColor(entry.festival.color(colorScheme))
                    .widgetAccentable(renderingMode == .accented)
                } else {
                    let lunar = entry.dayModel.date.lunarMonthString + entry.dayModel.date.lunarDayString(false)
                    Text(String(lunar))
                    .font(.system(size: 12, weight: .medium))
                    .foregroundColor(entry.normal.color(colorScheme))
                    .widgetAccentable(renderingMode == .accented)
                }
            }
            .padding(.leading, 3)
            Spacer()
            if entry.showFestival, entry.dayModel.tiaoxiu != .none {
                Image(entry.dayModel.tiaoxiu == .rest ? "rest" : "work")
                    .resizable()
                    .frame(width: 20, height: 22)
                    .foreground(color: entry.dayModel.tiaoxiu == .rest ? entry.rest.color(colorScheme) : entry.work.color(colorScheme))
            }
        }
    }

    func sectionHeader(_ entry: LargeEntry, title: String, color: Color) -> some View {
        HStack {
            color.frame(width: 4, height: 14).cornerRadius(2)
            Text(title)
                .font(.system(size: 12, weight: .bold))
                .foreground(color: entry.theme.tintColor)
                .widgetAccentable(renderingMode == .accented)
        }
    }

    func tagView(_ tags: [String], color: Color) -> some View {
        WrappingHStack(tags, spacing: .constant(4), lineSpacing: 4) { tag in
            ZStack {
                color.clipShape(.capsule)
                    .widgetAccentable(renderingMode == .accented)
                Text(tag).font(.system(size: 12, weight: .regular))
                .padding(.all, 4)
                .foreground(color: color.contrastColor)
            }
            .fixedSize()
        }
    }
}

private extension DayView {
    func normalColor(_ entry: TimelineEntry & CalendarEntryable) -> Color {
        entry.normal.color(colorScheme)
    }

    func weekColor(_ entry: TimelineEntry & CalendarEntryable, dayModel: DayModel) -> Color {
        if dayModel.date.isWeekDay {
            entry.week.color(colorScheme)
        } else {
            entry.normal.color(colorScheme)
        }
    }
}
